﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using NHibernateDB;
using System.Threading;

namespace SmartAnnotation
{
    public partial class SmartAnnotationForm : Form
    {
        private Bitmap                  bmp;
        private Annotation              annotationManager;
        private IList<AdnotariMedicale> admedList_orig;   // original list
        private IList<AdnotariMedicale> admedList_new;    // updated list
        private int                     IdImagine;
        private string                  NumeFisier;
        private string                  limba;
        private Thread                  annotationWatchDog;
        private int                     watchDogTimeOut;
        private string doctor;

        public SmartAnnotationForm(string IdImagine, string NumeFisier, string limba,string doctor)
        {
            // Init
            InitializeComponent();
            Rename(limba);

            this.IdImagine = Int32.Parse(IdImagine);
            this.NumeFisier = NumeFisier;
            this.limba = limba;
            this.doctor = doctor;

            // Init the image
            bmp = new Bitmap(NumeFisier);
            pictureBox1.Image = bmp;
            annotationManager = new Annotation(pictureBox1, vScrollBar, hScrollBar,doctor);

            // Get annotation from DB
            PersistenceManager pm = PersistenceManager.Instance;
            admedList_orig = pm.RetrieveEquals<AdnotariMedicale>("IdImagine", Int32.Parse(IdImagine));

            foreach (AdnotariMedicale admed in admedList_orig)
            {
                annotationManager.GetAnnotationListLOCK();

                IList<Login> ListDoctor = pm.RetrieveEquals<Login>("Id", admed.IdDoctor);

                annotationManager.AnnotationList.AddLast(new RectangleType(admed.Id, ListDoctor[0].Nume, admed.x, admed.y, admed.w, admed.h, admed.Titlu, admed.Descriere));
                annotationManager.ReleaseAnnotationListLOCK();
            }

            // Start thread to listen for changes in annotations
            annotationWatchDog = new Thread(new ThreadStart(AnnotationWatchDog));
            annotationWatchDog.Name = "Annotation WhatchDog";
            watchDogTimeOut = 1;
            annotationWatchDog.Start();
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            // Perform a cleanup
            bmp.Dispose();
            bmp = null;
            System.IO.File.Delete(NumeFisier);
            annotationWatchDog.Abort();
            this.Dispose();
        }

        private void buttonSave_Click(object sender, EventArgs e)
        {
            PersistenceManager pm = PersistenceManager.Instance;
            bool listModified = true; // This is used to repeate action "First try to find deleted annotations"
                                      // if one or more LinkedList is modified. If a LinkedList is modified
                                      // it will thow an exeption in a "foreach" statement.

            while (listModified)
            {
                listModified = false;

                annotationManager.GetAnnotationListLOCK();
                foreach (RectangleType rect in annotationManager.AnnotationList)
                {
                    if (rect.GetID() != 0)
                    {
                        foreach (AdnotariMedicale admed in admedList_orig)
                            if (rect.GetID() == admed.Id &&
                                (!rect.GetText().Equals(admed.Descriere) ||
                                !rect.GetTitle().Equals(admed.Titlu) ||
                                rect.GetX() != admed.x ||
                                rect.GetY() != admed.y ||
                                rect.GetWidth() != admed.w ||
                                rect.GetHeight() != admed.h))
                            {
                                //pm.Delete(admed);

                                //AdnotariMedicale am = new AdnotariMedicale();

                                //am.Titlu = rect.GetTitle();
                                //am.Descriere = rect.GetText();
                                //am.IdImagine = IdImagine;
                                //am.x = rect.GetX();
                                //am.y = rect.GetY();
                                //am.w = rect.GetWidth();
                                //am.h = rect.GetHeight();

                                //pm.Save(am);

                                IList<Login> listLogin = pm.RetrieveEquals<Login>("Nume", doctor);

                                admed.Titlu = rect.GetTitle();
                                admed.IdDoctor = listLogin[0].Id;
                                admed.Descriere = rect.GetText();
                                admed.IdImagine = IdImagine;
                                admed.x = rect.GetX();
                                admed.y = rect.GetY();
                                admed.w = rect.GetWidth();
                                admed.h = rect.GetHeight();

                                pm.Modify(admed, admed.Id);
                            }
                    }
                    else
                    {
                        AdnotariMedicale am = new AdnotariMedicale();
                        IList<Login> listLogin = pm.RetrieveEquals<Login>("Nume", doctor);

                        am.Titlu = rect.GetTitle();
                        am.IdDoctor = listLogin[0].Id;
                        am.Descriere = rect.GetText();
                        am.IdImagine = IdImagine;
                        am.x = rect.GetX();
                        am.y = rect.GetY();
                        am.w = rect.GetWidth();
                        am.h = rect.GetHeight();

                        pm.Save(am);

                        //annotationManager.AnnotationList.Remove(rect);
                        //listModified = true;
                        //break;
                    }
                }
                annotationManager.ReleaseAnnotationListLOCK();
            }

            bool found;
            foreach (AdnotariMedicale admed_orig in admedList_orig)
            {
                found = false;

                annotationManager.GetAnnotationListLOCK();
                foreach (AnnotationType admed_lc in annotationManager.AnnotationList)
                    if (admed_orig.Id == admed_lc.GetID())
                    {
                        found = true;
                        break;
                    }
                annotationManager.ReleaseAnnotationListLOCK();

                if (!found)
                    // => the user deleted the annotation
                    pm.Delete(admed_orig);
            }

            // Get annotation from DB
            pm = PersistenceManager.Instance;
            admedList_orig = pm.RetrieveEquals<AdnotariMedicale>("IdImagine", IdImagine);
            annotationManager.GetAnnotationListLOCK();
            annotationManager.AnnotationList.Clear();
            foreach (AdnotariMedicale admed in admedList_orig)
            {
                IList<Login> ListDoctor = pm.RetrieveEquals<Login>("Id", admed.IdDoctor);

                annotationManager.AnnotationList.AddLast(new RectangleType(admed.Id, ListDoctor[0].Nume, admed.x, admed.y, admed.w, admed.h, admed.Titlu, admed.Descriere));
                
            }
            annotationManager.ReleaseAnnotationListLOCK();
        }

        private void AnnotationWatchDog()
        {
            PersistenceManager pm = PersistenceManager.Instance;
            bool found;
            bool listModified;        // This is used to repeate action "First try to find deleted annotations"
                                      // if one or more LinkedList is modified. If a LinkedList is modified
                                      // it will thow an exeption in a "foreach" statement.

            while (true)
            {
                // Update every "watchDogTimeOut" seconds
                Thread.Sleep(TimeSpan.FromSeconds(watchDogTimeOut));
                admedList_new = pm.RetrieveEquals<AdnotariMedicale>("IdImagine", IdImagine);

                // First try to find deleted annotations
                //
                listModified = true;
                while (listModified) // case 1
                {
                    listModified = false;

                    annotationManager.GetAnnotationListLOCK();
                    foreach (RectangleType admed_lc in annotationManager.AnnotationList)
                    {
                        if (admed_lc.GetID() == 0)
                            // Annotation created by user, no need to continue
                            break;

                        found = false;
                        foreach (AdnotariMedicale admed_new in admedList_new)
                            if (admed_lc.GetID() == admed_new.Id)
                            {
                                found = true;
                                break;
                            }

                        if (!found)
                        // Not found => annotation deleted
                        {
                            foreach (AdnotariMedicale admed_orig in admedList_orig)
                            {
                                if (admed_lc.GetID() == admed_orig.Id)
                                {
                                    if (!admed_lc.GetText().Equals(admed_orig.Descriere) ||
                                        !admed_lc.GetTitle().Equals(admed_orig.Titlu) ||
                                        admed_lc.GetX() != admed_orig.x ||
                                        admed_lc.GetY() != admed_orig.y ||
                                        admed_lc.GetWidth() != admed_orig.w ||
                                        admed_lc.GetHeight() != admed_orig.h)
                                    {
                                        // Local copy != original, but the new is deleted.
                                        // In this case we will keep the local copy but the original will be deleted
                                        // because the local copy will be considerated as a new annotation.
                                        admedList_orig.Remove(admed_orig);
                                        listModified = true;
                                    }
                                    else
                                    {
                                        // Local copy == original => user didn't modified the annotation, so it's safe to remove.
                                        admedList_orig.Remove(admed_orig);
                                        annotationManager.AnnotationList.Remove(admed_lc);
                                        listModified = true;
                                    }
                                }

                                if (listModified)
                                    break;
                            } // foreach
                        } // if

                        if (listModified)
                            break;
                    } // foreach
                    annotationManager.ReleaseAnnotationListLOCK();
                } // while

                //foreach (AdnotariMedicale admed_orig in admedList_orig)
                //{
                //    found = false;
                //    foreach (RectangleType admed_lc in annotationManager.AnnotationList)
                //        if (admed_orig.Id == admed_lc.GetID())
                //        {
                //            found = true;
                //            break;
                //        }
                //}

                // Try then to find new annotations
                //
                foreach (AdnotariMedicale admed_new in admedList_new)
                {
                    found = false;

                    annotationManager.GetAnnotationListLOCK();
                    foreach (RectangleType admed_lc in annotationManager.AnnotationList)
                    {
                        if (admed_lc.GetID() == admed_new.Id)
                        {
                            found = true;
                            break;
                        }
                    }

                    if (!found)
                    {
                        found = false;

                        // Search in orig, if found => local copy modified so is not a new annotation
                        foreach (AdnotariMedicale admed_orig in admedList_orig)
                            if (admed_orig.Id == admed_new.Id)
                            {
                                found = true;
                                break;
                            }

                        if (!found)
                        {
                            // It is a new annotation, so add it to the list

                             IList<Login> ListDoctor = pm.RetrieveEquals<Login>("Id", admed_new.IdDoctor);

                            annotationManager.AnnotationList.AddLast(new RectangleType(admed_new.Id,
                                                                                       ListDoctor[0].Nume,
                                                                                       admed_new.x,
                                                                                       admed_new.y,
                                                                                       admed_new.w,
                                                                                       admed_new.h,
                                                                                       admed_new.Titlu,
                                                                                       admed_new.Descriere));
                            admedList_orig.Add(admed_new);
                            annotationManager.ReleaseAnnotationListLOCK(); // make sure we release the lock
                            break;
                        }
                    }
                    annotationManager.ReleaseAnnotationListLOCK();
                }

                // Try then to find modified annotations
                //
                foreach (AdnotariMedicale admed_new in admedList_new)
                {
                    annotationManager.GetAnnotationListLOCK();
                    foreach (RectangleType admed_lc in annotationManager.AnnotationList)
                    {
                        if (admed_lc.GetID() == admed_new.Id)
                            if ( !admed_lc.GetText().Equals(admed_new.Descriere) ||
                                 !admed_lc.GetTitle().Equals(admed_new.Titlu) ||
                                 admed_lc.GetX() != admed_new.x ||
                                 admed_lc.GetY() != admed_new.y ||
                                 admed_lc.GetWidth() != admed_new.w ||
                                 admed_lc.GetHeight() != admed_new.h )
                        // The annotation exist, but the local copy is different from the DB copy
                        {
                            // Get the annotation from original list
                            AdnotariMedicale admed_orig = null;
                            foreach (AdnotariMedicale a in admedList_orig)
                                if (a.Id == admed_new.Id)
                                {
                                    admed_orig = a;
                                    break;
                                }
                            if (admed_orig == null)
                                break;

                            // Validate X
                            //
                            if (admed_orig.x == admed_lc.GetX())
                            {
                                // Local copy was not modified => no conflict
                                admed_lc.SetX(admed_new.x);
                                admed_orig.x = admed_new.x;
                            }
                            else
                            {
                                // Local copy != orig
                                if (admed_orig.x == admed_new.x) { }
                                // New == orig => item modified only by this user => no conflict
                                // Local copy will be kept
                                else
                                {
                                    // New != orig != local copy => conflict !!!
                                    DialogResult res = MessageBox.Show("Coordonate X locala: " + admed_lc.GetX() +
                                                                        "\r\nCoordonate X noua: " + admed_new.x +
                                                                        "\r\nDoriti sa pastrati copia locala?",
                                                                        "Conflict detectat!",
                                                                        MessageBoxButtons.YesNo,
                                                                        MessageBoxIcon.Warning);
                                    if (res == DialogResult.No)
                                    {
                                        // Modify local copy
                                        admed_lc.SetX(admed_new.x);
                                        admed_orig.x = admed_new.x;
                                    }
                                    else
                                    {
                                        // Set the new orig to get rid of constant pop-up boxes
                                        admed_orig.x = admed_new.x;
                                    }
                                }
                            }

                            // Validate Y
                            //
                            if (admed_orig.y == admed_lc.GetY())
                            {
                                // Local copy was not modified => no conflict
                                admed_lc.SetY(admed_new.y);
                                admed_orig.y = admed_new.y;
                            }
                            else
                            {
                                // Local copy != orig
                                if (admed_orig.y == admed_new.y) { }
                                // New == orig => item modified only by this user => no conflict
                                // Local copy will be kept
                                else
                                {
                                    // New != orig != local copy => conflict !!!
                                    DialogResult res = MessageBox.Show("Coordonate Y locala: " + admed_lc.GetY() +
                                                                        "\r\nCoordonate Y noua: " + admed_new.y +
                                                                        "\r\nDoriti sa pastrati copia locala?",
                                                                        "Conflict detectat!",
                                                                        MessageBoxButtons.YesNo,
                                                                        MessageBoxIcon.Warning);
                                    if (res == DialogResult.No)
                                    {
                                        // Modify local copy
                                        admed_lc.SetY(admed_new.y);
                                        admed_orig.y = admed_new.y;
                                    }
                                    else
                                    {
                                        // Set the new orig to get rid of constant pop-up boxes
                                        admed_orig.y = admed_new.y;
                                    }
                                }
                            }

                            // Validate W
                            //
                            if (admed_orig.w == admed_lc.GetWidth())
                            {
                                // Local copy was not modified => no conflict
                                admed_lc.SetWidth(admed_new.w);
                                admed_orig.w = admed_new.w;
                            }
                            else
                            {
                                // Local copy != orig
                                if (admed_orig.w == admed_new.w) { }
                                // New == orig => item modified only by this user => no conflict
                                // Local copy will be kept
                                else
                                {
                                    // New != orig != local copy => conflict !!!
                                    DialogResult res = MessageBox.Show("Coordonate W locala: " + admed_lc.GetWidth() +
                                                                        "\r\nCoordonate W noua: " + admed_new.w +
                                                                        "\r\nDoriti sa pastrati copia locala?",
                                                                        "Conflict detectat!",
                                                                        MessageBoxButtons.YesNo,
                                                                        MessageBoxIcon.Warning);
                                    if (res == DialogResult.No)
                                    {
                                        // Modify local copy
                                        admed_lc.SetWidth(admed_new.w);
                                        admed_orig.w = admed_new.w;
                                    }
                                    else
                                    {
                                        // Set the new orig to get rid of constant pop-up boxes
                                        admed_orig.w = admed_new.w;
                                    }
                                }
                            }

                            // Validate H
                            //
                            if (admed_orig.h == admed_lc.GetHeight())
                            {
                                // Local copy was not modified => no conflict
                                admed_lc.SetHeight(admed_new.h);
                                admed_orig.h = admed_new.h;
                            }
                            else
                            {
                                // Local copy != orig
                                if (admed_orig.h == admed_new.h) { }
                                // New == orig => item modified only by this user => no conflict
                                // Local copy will be kept
                                else
                                {
                                    // New != orig != local copy => conflict !!!
                                    DialogResult res = MessageBox.Show("Coordonate H locala: " + admed_lc.GetHeight() +
                                                                        "\r\nCoordonate H noua: " + admed_new.h +
                                                                        "\r\nDoriti sa pastrati copia locala?",
                                                                        "Conflict detectat!",
                                                                        MessageBoxButtons.YesNo,
                                                                        MessageBoxIcon.Warning);
                                    if (res == DialogResult.No)
                                    {
                                        // Modify local copy
                                        admed_lc.SetHeight(admed_new.h);
                                        admed_orig.h = admed_new.h;
                                    }
                                    else
                                    {
                                        // Set the new orig to get rid of constant pop-up boxes
                                        admed_orig.h = admed_new.h;
                                    }
                                }
                            }

                            // Validate Titlu
                            //
                            if (admed_orig.Titlu.Equals(admed_lc.GetTitle()))
                            {
                                // Local copy was not modified => no conflict
                                admed_lc.SetTitle(admed_new.Titlu);
                                admed_orig.Titlu = admed_new.Titlu;
                            }
                            else
                            {
                                // Local copy != orig
                                if (admed_orig.Titlu.Equals(admed_new.Titlu)) { }
                                // New == orig => item modified only by this user => no conflict
                                // Local copy will be kept
                                else
                                {
                                    // New != orig != local copy => conflict !!!
                                    DialogResult res = MessageBox.Show("Titlu local: " + admed_lc.GetTitle() +
                                                                        "\r\nTitlu nou: " + admed_new.Titlu +
                                                                        "\r\nDoriti sa pastrati copia locala?",
                                                                        "Conflict detectat!",
                                                                        MessageBoxButtons.YesNo,
                                                                        MessageBoxIcon.Warning);
                                    if (res == DialogResult.No)
                                    {
                                        // Modify local copy
                                        admed_lc.SetTitle(admed_new.Titlu);
                                        admed_orig.Titlu = admed_new.Titlu;
                                    }
                                    else
                                    {
                                        // Set the new orig to get rid of constant pop-up boxes
                                        admed_orig.Titlu = admed_new.Titlu;
                                    }
                                }
                            }

                            // Validate Descriere
                            //
                            if (admed_orig.Descriere.Equals(admed_lc.GetText()))
                            {
                                // Local copy was not modified => no conflict
                                admed_lc.SetText(admed_new.Descriere);
                                admed_orig.Descriere = admed_new.Descriere;
                            }
                            else
                            {
                                // Local copy != orig
                                if (admed_orig.Descriere.Equals(admed_new.Descriere)) { }
                                // New == orig => item modified only by this user => no conflict
                                // Local copy will be kept
                                else
                                {
                                    // New != orig != local copy => conflict !!!
                                    DialogResult res = MessageBox.Show("Descriere locala: " + admed_lc.GetText() +
                                                                        "\r\nDescriere noua: " + admed_new.Descriere +
                                                                        "\r\nDoriti sa pastrati copia locala?",
                                                                        "Conflict detectat!",
                                                                        MessageBoxButtons.YesNo,
                                                                        MessageBoxIcon.Warning);
                                    if (res == DialogResult.No)
                                    {
                                        // Modify local copy
                                        admed_lc.SetText(admed_new.Descriere);
                                        admed_orig.Descriere = admed_new.Descriere;
                                    }
                                    else
                                    {
                                        // Set the new orig to get rid of constant pop-up boxes
                                        admed_orig.Descriere = admed_new.Descriere;
                                    }
                                }
                            }

                        } // else if
                    } // foreach
                    annotationManager.ReleaseAnnotationListLOCK();
                } // foreach
            } // while
        } // method AnnotationWhatchDog
    } // class ...
} // namespace ...
